package mysql

import (
	"fmt"
	"github.com/go-xorm/xorm"
	"github.com/pkg/errors"
	"strings"
	"xorm.io/builder"
)

// 预处理 SQL 操作数据
func (h *Handler) pretreatment(info *SqlInfo, sess ...*xorm.Session) (session *xorm.Session, err error) {
	// 1. 检查参数是否为空
	if info.Obj == nil {
		return session, errors.New("pretreatment：sql operation object cannot be empty")
	}
	if h.EngineGroup == nil && h.Engine == nil {
		return session, errors.New("pretreatment：xorm engine cannot be empty")
	}
	// 2. 初始化操作引擎
	if sess != nil { // 判断是否事务操作
		session = sess[0]
	} else if h.EngineGroup != nil { // 判断是否为主从引擎组
		session = h.EngineGroup.NewSession()
	} else if h.Engine != nil {
		session = h.Engine.NewSession()
	} else {
		return session, errors.New("xorm engine cannot be empty")
	}
	if info.Ctx != nil {
		session = session.Context(info.Ctx)
	}
	// 3. 检查参数是否存在 SQL 注入
	if len(info.Field) > 0 {
		field := strings.Join(info.Field, ",")
		if err := filterColumns([]string{field}); err != nil {
			return session, err
		}
		session = session.Select(field)
	}
	if len(info.Cols) > 0 {
		if err := filterColumns(info.Cols); err != nil {
			return session, err
		}
		session = session.Cols(info.Cols...)
	}
	if len(info.Omit) > 0 {
		if err := filterColumns(info.Omit); err != nil {
			return session, err
		}
		session = session.Omit(info.Omit...)
	}
	if info.Order != "" {
		if err = h.filterOrderBy(getTable(info), info.Order, info.Field); err != nil {
			return session, err
		}
	}
	if info.Group != "" {
		if err = h.filterGroupBy(info.Table, info.Group, info.Field); err != nil {
			return session, err
		}
	}
	// 4. 组装 SQL 操作
	err = buildSession(session, info)
	return
}

// 获取当前所有操作表名称
func getTable(info *SqlInfo) map[string]string {
	table := make(map[string]string, 0)
	if len(info.Join) > 0 {
		for k, v := range info.Join {
			if v.Alias != "" {
				table[v.Alias] = v.Table
			} else {
				key := fmt.Sprintf("AAA:%d", k)
				table[key] = v.Table
			}
		}
	}
	if info.Alias != "" {
		table[info.Alias] = info.Table
	} else {
		table["master"] = info.Table
	}
	return table
}

// 组装 SQL 操作的具体信息
func buildSession(session *xorm.Session, info *SqlInfo) (err error) {
	// 设置操作表名称
	if info.Table != "" {
		session = session.Table(info.Table)
	}
	if info.Alias != "" {
		session = session.Alias(info.Alias)
	}
	// 组装并设置 SQL 条件
	session = session.NoAutoCondition(info.AutoCondition)
	if info.Id != 0 {
		session = session.Id(info.Id)
	}
	if len(info.Where) != 0 {
		where, err := buildCondition(info.Where)
		if err != nil {
			return err
		}
		session = session.Where(builder.And(where...))
	}
	if info.And != nil {
		where, err := buildCondition(info.And.Condition)
		if err != nil {
			return err
		}
		if strings.ToUpper(info.And.ConnType) == "OR" {
			session = session.And(builder.Or(where...))
		} else {
			session = session.And(builder.And(where...))
		}
	}
	if info.Or != nil {
		where, err := buildCondition(info.Or.Condition)
		if err != nil {
			return err
		}
		if strings.ToUpper(info.Or.ConnType) == "OR" {
			session = session.Or(builder.Or(where...))
		} else {
			session = session.Or(builder.And(where...))
		}
	}
	if info.AndChild != nil {
		where, err := buildChildCondition(info.AndChild.Condition)
		if err != nil {
			return err
		}
		if strings.ToUpper(info.AndChild.ConnType) == "OR" {
			session = session.And(builder.Or(where...))
		} else {
			session = session.And(builder.And(where...))
		}
	}
	if info.OrChild != nil {
		where, err := buildChildCondition(info.OrChild.Condition)
		if err != nil {
			return err
		}
		if strings.ToUpper(info.OrChild.ConnType) == "OR" {
			session = session.Or(builder.Or(where...))
		} else {
			session = session.Or(builder.And(where...))
		}
	}
	// 设置排序条件
	if info.Order != "" {
		session = session.OrderBy(info.Order)
	}
	// 设置分组条件
	if info.Group != "" {
		session = session.GroupBy(info.Group)
	}
	// 注释 Having 条件
	//if info.Having != "" {
	//	session = session.Having(info.Having)
	//}
	// 设置分页查询条件
	if info.Rows == 0 {
		info.Rows = 100
	}
	if info.Start == 0 && info.Page != 0 {
		info.Start = (info.Page - 1) * info.Rows
	}
	session = session.Limit(info.Rows, info.Start)
	// 判断是否为连表查询
	if len(info.Join) > 0 {
		if info.Table == "" {
			return errors.New("buildSession：sql table parameter is not allowed to be empty")
		}
		for _, v := range info.Join {
			tableName := []string{v.Table}
			if v.Alias != "" {
				tableName = append(tableName, v.Alias)
			}
			if v.Args != nil {
				session = session.Join(v.Type, tableName, v.On, v.Args...)
			} else {
				session = session.Join(v.Type, tableName, v.On)
			}
		}
	}
	return nil
}
